#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 时间     : 2018-06-09 16:08:16
# 作者     : DL (584054432@qq.com)
# 网址     : https://gitee.com/dingliangPython/
# 软件版本 : Python3.6.5
# 功能     ：

import cx_Oracle
import sys
import re
import threading
import time
import logging
if sys.version_info.major == 3 and sys.version_info.minor <= 4:
    from DBUtils.PooledDB import PooledDB
else:
    from dbutils.pooled_db import PooledDB
import gc
"""摘要

功能描述：连接池,及普通连接
"""


class PoolConnect():
    pool=None
    status=None
    """摘要

    功能描述：通过连接池连接ORACLE
    """

    def __init__(self, arg):
        """摘要

        功能描述：初始化

        参数:
            arg: [描述: 数据库配置dict]
            log: [描述: 日志模块对象]
        """
        super(PoolConnect, self).__init__()
        self.arg = arg
        self.log = logging.getLogger("main.oracle")
        self.pool = self.connect()
        self.th = threading.Thread(target=self.resetConn)
        self.th.setDaemon(True)
        self.th.start()

    def connect(self):
        """摘要

        功能描述：连接到数据库连接池

        返回:
            [返回值描述: 连接池对象]
            [类型]
        """
        if self.arg['host']:
            dsn = self.arg['host'] + ":" + \
                str(self.arg['port']) + "/" + self.arg['database']
            try:
                pool = PooledDB(
                    creator=cx_Oracle,  # 使用链接数据库的模块
                    user=self.arg['user'],
                    password=self.arg['password'],
                    dsn=dsn,
                    # 连接池允许的最大连接数，0和None表示没有限制
                    maxconnections=int(self.arg['maxconnections']),
                    # 初始化时，连接池至少创建的空闲的连接，0表示不创建
                    mincached=int(self.arg['mincached']),
                    # 连接池空闲的最多连接数，0和None表示没有限制
                    maxcached=int(self.arg['maxcached']),
                    # 连接池中最多共享的连接数量，0和None表示全部共享
                    maxshared=int(self.arg['maxshared']),
                    blocking=True  # 链接池中如果没有可用共享连接后，是否阻塞等待，True表示等待，False表示不等待然后报错
                    # login_timeout=1

                )
                self.log.info('Oracle连接池初始成功')
                self.status=True
                return pool
            except Exception as e:
                self.log.error("Oracle连接池初始失败，错误信息：%s" % e)
                self.status=False
                return -1

    def loadSqlDict(self, sql):
        """摘要

        功能描述：查询语句

        参数:
            sql: [描述: sql语句]

        返回:
            [返回值描述: 返回查询dict]
            [类型]
        """
        try:
            connect = self.pool.connection()
            cursor = connect.cursor()
            data = []
            cursor.execute(sql)
            value = cursor.fetchall()
            if value and len(value) >= 1:
                field = tuple([d[0] for d in cursor.description])
                for v in value:
                    data.append(dict(zip(field, v)))
                cursor.close()
                connect.close()
                return data
            else:
                # self.log.info("查询数据0条")
                return 0
        except Exception as e:
            self.log.warning("查询数据:%s 错误：%s" % (sql, e))
            errorMsg = re.findall('connection', e)
            if errorMsg and errorMsg[0]:
                self.log.warning("数据库异常，重新连接")
                self.pool = self.connect()
            return -1

    def loadSqlList(self, sql):
        """摘要

        功能描述：查询数据库

        参数:
            sql: [描述: SQL语句]

        返回:
            [返回值描述: 返回查询列表,不带字段名]
            [类型]
        """
        try:
            connect = self.pool.connection()
            cursor = connect.cursor()
            data = []
            cursor.execute(sql)
            value = cursor.fetchall()
            if value and len(value) >= 1:
                data = value
                cursor.close()
                connect.close()
                return data
            else:
                # self.log.info("查询数据0条2")
                return 0
        except Exception as e:
            self.log.warning("查询数据:%s 错误：%s " % (sql, e))
            return False

    def execSql(self, sql):
        """摘要

        功能描述：执行数据语句

        参数:
            sql: [描述: SQL语句]

        返回:
            [返回值描述: 执行成功条数]
            [类型]
        """
        try:
            connect = self.pool.connection()
            cursor = connect.cursor()
            cursor.execute(sql)
            rec = cursor.rowcount
            connect.commit()
            #self.log.info("执行语句：%s 2"%sql)
            if rec >= 1:
                # self.log.info("数据执行成功，影响%d行 "%rec)
                cursor.close()
                connect.close()
                return rec
            else:
                # self.log.info("数据执行成功，影响%d行"%rec)
                return False
        except Exception as e:
            self.log.warning("执行数据失败：%s,%s " % (sql, e))

    def execSqlList(self, sql, values):
        """摘要

        功能描述：批量执行

        参数:
            sql: [描述: 占位符SQL语句] 例如 INSERT INTO T (A,B) VALUES(:1,:2) 占位符为：1，非%s或{}
            values: [描述: 列表SQL语句值] 例如 [(11,22),(12,22)]


        返回:
            [返回值描述:返回执行成功条数 ]
            [类型]
        """
        try:
            connect = self.pool.connection()
            cursor = connect.cursor()
            # cursor.prepare(sql)
            cursor.executemany(sql, values)
            rec = cursor.rowcount
            connect.commit()
            # self.log.debug("执行语句：%s 2"%sql)
            if rec >= 1:
                # self.log.info("数据执行成功，影响%d行 "%rec)
                cursor.close()
                connect.close()
                return rec
            else:
                # self.log.debug("数据执行成功，影响%d行"%rec)
                return False
        except Exception as e:
            self.log.warning("执行数据失败：%s " % e)

    def resetConn(self):
        """摘要

        功能描述：断线重连线程
        """
        while True:
            rec = self.loadSqlList("SELECT SYSDATE FROM DUAL")
            try:
                if not rec:
                    self.log.debug("重新连接数据库。。。")
                    self.pool = self.connect()
                else:
                    self.log.debug("Oralc 数据库连接池正常\n\n")
            except BaseException as e:
                self.log.error("重连数据库失败，检测连接参数,数据库服务,通信？？ 异常：%s" % e)
            del rec
            gc.collect()
            time.sleep(300)
# 短连接


class Connect(object):
    """摘要

    功能描述：短连接到数据库
    """

    def __init__(self, arg):
        """摘要

        功能描述：初始化

        参数:
            arg: [描述: 数据库配置dict]
            log: [描述: 日志模块对象]
        """
        super(Connect, self).__init__()
        self.arg = arg
        self.log = logging.getLogger("main.oracle")
        self.conn=self.connect()
        # self.conn.close()

    def connect(self):
        """摘要

        功能描述：连接到数据库

        返回:
            [返回值描述: 连接对象]
            [类型]
        """
        try:
            conn = cx_Oracle.connect("%s/%s@%s:%s/%s" % (self.arg['user'], self.arg[
                                     'password'], self.arg['host'], self.arg['port'], self.arg['database']))
            # self.log.info("Oracle连接成功")
            return conn
        except Exception as e:
            self.log.error("连接数据库失败")
            return -1

    def loadSqlDict(self, sql):
        """摘要

        功能描述：查询语句

        参数:
            sql: [描述: sql语句]

        返回:
            [返回值描述: 返回查询dict]
            [类型]
        """
        try:
            connect = self.connect()
            cursor = connect.cursor()
            data = []
            cursor.execute(sql)
            value = cursor.fetchall()
            if value and len(value) >= 1:
                field = tuple([d[0] for d in cursor.description])
                for v in value:
                    data.append(dict(zip(field, v)))
                cursor.close()
                connect.close()
                return data
            else:
                # self.log.info("查询数据0条")
                return 0
        except Exception as e:
            self.log.warning("查询数据:%s 错误：%s" % (sql, e))
            return -1

    def loadSqlList(self, sql):
        """摘要

        功能描述：查询数据库

        参数:
            sql: [描述: SQL语句]

        返回:
            [返回值描述: 返回查询列表,不带字段名]
            [类型]
        """
        try:
            connect = self.connect()
            cursor = connect.cursor()
            data = []
            cursor.execute(sql)
            value = cursor.fetchall()
            if value and len(value) >= 1:
                data = value
                cursor.close()
                connect.close()
                return data
            else:
                # self.log.info("查询数据0条2")
                return 0
        except Exception as e:
            self.log.warning("查询数据:%s 错误：%s " % (sql, e))
            return -1

    def execSql(self, sql):
        """摘要

        功能描述：执行数据语句

        参数:
            sql: [描述: SQL语句]

        返回:
            [返回值描述: 执行成功条数]
            [类型]
        """
        try:
            connect = self.connect()
            cursor = connect.cursor()
            cursor.execute(sql)
            rec = cursor.rowcount
            connect.commit()
            #self.log.info("执行语句：%s 2"%sql)
            if rec >= 1:
                # self.log.info("数据执行成功，影响%d行 "%rec)
                cursor.close()
                connect.close()
                return rec
            else:
                # self.log.info("数据执行成功，影响%d行"%rec)
                return 0
        except Exception as e:
            self.log.warning("执行数据失败：%s,%s " % (sql, e))
            return -1

    def execSqlList(self, sql, values):
        """摘要

        功能描述：批量执行

        参数:
            sql: [描述: 占位符SQL语句] 例如 INSERT INTO T (A,B) VALUES(:1,:2) 占位符为：1，非%s或{}
            values: [描述: 列表SQL语句值] 例如 [(11,22),(12,22)]


        返回:
            [返回值描述:返回执行成功条数 ]
            [类型]
        """
        try:
            connect = self.connect()
            cursor = connect.cursor()
            cursor.executemany(sql, values)
            rec = cursor.rowcount
            connect.commit()
            #self.log.debug("执行语句：%s 2"%sql)
            if rec >= 1:
                # self.log.info("数据执行成功，影响%d行 "%rec)
                cursor.close()
                connect.close()
                return rec
            else:
                # self.log.debug("数据执行成功，影响%d行"%rec)
                return False
        except Exception as e:
            self.log.warning("执行数据失败：%s " % e)
            return -1

